import sys
from lxml import etree


             
class Map:
    """Object for storing map data such as markers, points,
        which form various paths, center point, etc...
    """
    # TODO: how hard is implementing the following:
    # * get here pointers to user-clicked elements on the map, namely GPS points (some onClick() trap in google's javascript libraries?)
    # * get here user's mouse window coordinates upon a mouse click
    # * perform google API queries ala "addresses of and distances to nearest 5 hospitals, given a user-selected point"
    # ** all the above might be necessary to do on the fly in javascript rather than here
    #
    # + move to version 3 of the API including local search implemented as googlebar
    #   http://code.google.com/apis/ajaxsearch/documentation/
    #
    #
    
    def __init__(self, option):
        self.paths = []
        self.center = None
        self.minLat = sys.float_info.max
        self.minLon = sys.float_info.max
        self.maxLat = -sys.float_info.max
        self.maxLon = -sys.float_info.max

        # the following two switches are checked in the order given:
        # whether to create GPolyline in ecoded form or not
        self.ENCODED_POLYLINES = 'ENCODED_POLYLINES' == option
        # allow user to modify the drawn paths?
        self.MODIFIABLE_POLYLINES = 'MODIFIABLE_POLYLINE' == option
        # each second, represent a person's position via a mark
        self.ANIMATED_MARKS = 'ANIMATED_MARKS' == option
        # incrementally draw paths in time or draw them all instantnly?
        self.ANIMATED_POLYLINES = 'ANIMATED_POLYLINES' == option
        
        self.timeStep = 50 # for unfolding paths in time (miliseconds)
        
	# line colors
	self.COLORS = ("#FF0000", "#00FF00", "#0000FF", "#FFFF00", "#00FFFF",
                       "#FF00FF", "#0F0F00", "#00FF99", "#cd25cf", "#0099FF",
                       "#ffffff", "#ff690f", "#007fe6", "#001894", "#0fffad",
                       "#0a741f", "#4d0aa8", "#000000"  )
     
	self.HTML_TEMPLATE = open('page.html', 'r').read()
        self.parser = etree.XMLParser(dtd_validation=False, no_network=False)

    def validateHTML(self, string):
        etree.fromstring(string, self.parser)
    
    def setCenter(self):
    	#print self.paths[0].points[0][1]
    	#print self.paths[0].points[0][0]
    	self.center = (self.paths[0].points[0][1], self.paths[0].points[0][0])
        
           
    def addPath(self, path): 
   	self.paths.append(path)
        # find global min and max latitude and longitude for the whole set of paths
        self.minLon = min(self.minLon, path.minLon)
        self.maxLon = max(self.maxLon, path.maxLon)
        self.minLat = min(self.minLat, path.minLat)
        self.maxLat = max(self.maxLat, path.maxLat)
        #print self.minLon,  self.maxLon,  self.minLat,  self.maxLat
        self.center = ((self.maxLat + self.minLat)/2, (self.maxLon + self.minLon)/2)
        #print self.center

    #def distanceBetweenPointsInMeters(self, path1_idx, path2_idx, path1_point_idx, path2_point_idx):
    #    ...
    #    return (2.45985321 * Math.Acos(Math.Sin(lat1 / 57.2958) * Math.Sin(lat2 / 57.2958)
    #           + Math.Cos(lat1 / 57.2958) * Math.Cos(lat2 / 57.2958) *
    #             Math.Cos(lon2 / 57.2958 - lon1 / 57.2958)))

        
    def getPaths(self):
    	return self.paths
    
    def set_markers(self, markers):
        self.markers = markers        
    def to_html(self):
        if (self.ENCODED_POLYLINES):
            return  self.HTML_TEMPLATE % (self.center[0], self.center[1], self._encoded_polylines_to_js())


        elif (self.MODIFIABLE_POLYLINES):
             return self.HTML_TEMPLATE % ("", self.minLat, self.minLon, self.maxLat, self.maxLon, self.center[0], self.center[1],self.center[0], self.center[1], self._modifiable_polylines_to_js())


        elif (self.ANIMATED_MARKS):
           return self.HTML_TEMPLATE % (self.globalMarkDeclarations(), self.minLat, self.minLon, self.maxLat, self.maxLon, self.center[0], self.center[1],self.center[0], self.center[1], self._animated_marks_to_js())
        

        elif (self.ANIMATED_POLYLINES):
            return self.HTML_TEMPLATE % (self.globalLineDeclarations(), self.minLat, self.minLon, self.maxLat, self.maxLon, self.center[0], self.center[1],self.center[0], self.center[1], self._animated_polylines_to_js())


        else: 
            return self.HTML_TEMPLATE % ("", self.minLat, self.minLon, self.maxLat, self.maxLon, self.center[0], self.center[1],self.center[0], self.center[1], self._polylines_to_js())


    def globalMarkDeclarations(self):
        s=""
        for i in range(0, len(self.paths)):
            s += "var m"+str(i)+"; "
        return s
    
    def globalLineDeclarations(self):
        s=""
        for i in range(0, len(self.paths)):
            s += "var l"+str(i)+"; "
        return s


    def colorToNumber(self,colorstring):
        """ convert RRGGBB to an number representing the lightness of the color """
        colorstring = colorstring.strip()
        if colorstring[0] == '#': colorstring = colorstring[1:]
        if len(colorstring) != 6:
            raise ValueError, "input #%s is not in #RRGGBB format" % colorstring
        r, g, b = colorstring[:2], colorstring[2:4], colorstring[4:]
        r, g, b = [int(n, 16) for n in (r, g, b)]
        return r+g+b

# TODO:
#      add slider to adjust animation speed
#      add pause
#      add mouse-modification and anotation capability of paths when paused
#      delete already processed google.maps.latLngs

    def _animated_marks_to_js(self):
        """ visualize the paths by moving colored marks (pins) over the map in time """
        s = ""

        # one team has markers without stars ...
        s += "var baseIcon = new google.maps.Icon(G_DEFAULT_ICON);"
#        s += "baseIcon.shadow = \"http://www.google.com/mapfiles/shadow50.png\";"
        s += "baseIcon.shadow = \"http://chart.apis.google.com/chart?chst=d_map_pin_shadow\";"
        s += "baseIcon.iconSize = new google.maps.Size(15, 25);"
        s += "baseIcon.shadowSize = new google.maps.Size(34, 25);"
        s += "baseIcon.iconAnchor = new google.maps.Point(7, 25);"
        s += "baseIcon.infoWindowAnchor = new google.maps.Point(9, 2);\n"

        # ... and the other team has stars
        s += "var baseIcon2 = new google.maps.Icon(G_DEFAULT_ICON);"
        s += "baseIcon2.shadow = \"http://chart.apis.google.com/chart?chst=d_map_xpin_shadow&chld=pin_star\";"
        s += "baseIcon2.iconSize = new google.maps.Size(15, 25);"
        s += "baseIcon2.shadowSize = new google.maps.Size(34, 25);"
        s += "baseIcon2.iconAnchor = new google.maps.Point(7, 25);"
        s += "baseIcon2.infoWindowAnchor = new google.maps.Point(9, 2);\n"
            
        for i, path in enumerate(self.paths):
            wait = 1
            #remove leading #
            markColor = (self.COLORS[i % len(self.COLORS)])[1:]

            # if too dark a pin, write white text on it
            if (self.colorToNumber(markColor) < 180):
                textColor = "ffffff"
            else:
                textColor = "000000"

            mark = "m"+str(i)

            if (i<7):
                # team 1
                s += "var i"+str(i)+" = new google.maps.Icon(baseIcon);"
                s += "i"+str(i)+".image = \"http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld="
                s += chr(ord('A')+i)+"|"+markColor+"|"+textColor+"\";"
            else:
                # team 2
                s += "var i"+str(i)+" = new google.maps.Icon(baseIcon2);"
                s += "i"+str(i)+".image = \"http://chart.apis.google.com/chart?chst=d_map_xpin_letter&chld=pin_star|"
                s += chr(ord('A')+i)+"|"+markColor+"|"+textColor+"|00ff00\";"
            
            s += mark
            s += "=new google.maps.Marker(new google.maps.LatLng(%s, %s)," % (path.points[0][1], path.points[0][0])
            s += "{icon: i"+str(i)+", clickable: false, draggable: false, bouncy: false}); map.addOverlay("+mark+");\n"

            # schedule asynchronous display of all markers
            for pair in path.points:
                s += "setTimeout(\""+mark+".setLatLng("
                s += "new google.maps.LatLng(%s, %s))\"," % (pair[1], pair[0])
                s += str(wait)+");\n"
                wait += self.timeStep
        return s


    
    def _animated_polylines_to_js(self):
        """ visualize the paths by incrementally drawing the paths over the map over time """
        s = ""
        for i, path in enumerate(self.paths):
            t = 0 # index in time (for insertVertex())
            wait = 1
            color = self.COLORS[i % len(self.COLORS)]
            line = "l"+str(i)
            s += line+"=new google.maps.Polyline(null, \""+color+"\", 2, 0.8); map.addOverlay("+line+");\n"

            for pair in path.points:
                s += "setTimeout(\""+line+".insertVertex("+str(t)
                s += ", new google.maps.LatLng(%s, %s))\"," % (pair[1], pair[0])
                s += str(wait)+");\n"
                wait += self.timeStep
                t += 1
        return s


    # very slow for paths with many points
    def _modifiable_polylines_to_js(self):
        s = ""
        for i, path in enumerate(self.paths):
            color = self.COLORS[i % len(self.COLORS)]
            line = "l"+str(i)
            s += "var "+line+"=new google.maps.Polyline(["
            for pair in path.points:
                s += "new google.maps.LatLng(%s, %s)," % (pair[1], pair[0])
            s += "], \""+color+"\", 2, 0.8); map.addOverlay("+line+");\n"
            s += line+".enableDrawing({maxVertices: 1024});"
        return s

    
    def _polylines_to_js(self):
        js = ""
        for i, path in enumerate(self.paths):
            
            pointArray = "["
            for pair in path.points:
                pointArray += "new google.maps.LatLng(%s, %s)," % (pair[1], pair[0])
            pointArray += "]"
            js += """map.addOverlay(new google.maps.Polyline(%s, "%s", 1, 0.8));\n""" % (pointArray, self.COLORS[i % len(self.COLORS)])
            
        return js



    
    def _encoded_polylines_to_js(self):
        import gpolyencode
        encoder = gpolyencode.GPolyEncoder()
        
        js = ""
       
        for i, path in enumerate(self.paths):
            # GPolyEncoder expects switched lat/lng
            encoded_points = encoder.encode(path.points)
            
            js += """map.addOverlay(new google.maps.Polyline.fromEncoded({
                color: "%s",
                weight: 3,
                opacity: 1,
                points: "%s",
                levels: "%s",
                zoomFactor: %s,
                numLevels: %s
            }));
            """ % (self.COLORS[i % len(self.COLORS)], encoded_points['points'], encoded_points['levels'], encoded_points['zoomFactor'], encoded_points['numLevels'])
            #print self.COLORS[i % len(self.COLORS)], encoded_points['points'], encoded_points['levels'], encoded_points['zoomFactor'], encoded_points['numLevels']
        return js



    
    def _markers_to_js(self):
        js = ""
        for path in self.paths:
            for pair in path.points:
                js += """map.addOverlay(new google.maps.Marker(new google.maps.LatLng(%s, %s)));\n""" % (pair[0], pair[1])    
        return js
        
        
        
     
        
